`include "defines.d"

module TOP;

	// Probe the simulation
	
//	initial begin
//		$shm_open();
//		$shm_probe(TOP,"AMC");
//	end

	reg CLK;
	reg RESET;	
	
	initial begin
		CLK = 1;
		forever begin
			`PHASE
			CLK = 0;
			`PHASE
			CLK = 1;
		end
	end
	
	initial begin
		RESET = 0;
		`PHASE
		`TICK
		RESET = 1;
	
	end
	

	
	/* Network Testbench */
	// 4x4 
	
	// input regs
	reg [`DATA_WIDTH-1:0] data_in_0_0, data_in_0_1, data_in_1_0, data_in_1_1;
	reg valid_in_0_0, valid_in_0_1, valid_in_1_0, valid_in_1_1;
	reg credit_in_0_0, credit_in_0_1, credit_in_1_0, credit_in_1_1;
	
	// output wires
	wire [`DATA_WIDTH-1:0] data_out_0_0, data_out_0_1, data_out_1_0, data_out_1_1;
	wire valid_out_0_0, valid_out_0_1, valid_out_1_0, valid_out_1_1;
	wire credit_out_0_0, credit_out_0_1, credit_out_1_0, credit_out_1_1;
	
	network NETWORK (
	// Primary inputs
	 .data_in_0_0(data_in_0_0), .data_in_0_1(data_in_0_1), .data_in_1_0(data_in_1_0), .data_in_1_1(data_in_1_1),
	 .valid_in_0_0(valid_in_0_0), .valid_in_0_1(valid_in_0_1), .valid_in_1_0(valid_in_1_0), .valid_in_1_1(valid_in_1_1),
	 .credit_in_0_0(credit_in_0_0), .credit_in_0_1(credit_in_0_1), .credit_in_1_0(credit_in_1_0), .credit_in_1_1(credit_in_1_1),	
	// Primary outputs
	 .data_out_0_0(data_out_0_0), .data_out_0_1(data_out_0_1), .data_out_1_0(data_out_1_0), .data_out_1_1(data_out_1_1),
	 .valid_out_0_0(valid_out_0_0), .valid_out_0_1(valid_out_0_1), .valid_out_1_0(valid_out_1_0), .valid_out_1_1(valid_out_1_1),
	 .credit_out_0_0(credit_out_0_0), .credit_out_0_1(credit_out_0_1), .credit_out_1_0(credit_out_1_0), .credit_out_1_1(credit_out_1_1),	
	// global network inputs
	.CLK(CLK), .RESET(RESET)
	);
	
	initial begin
		data_in_0_1 = 34'b0; data_in_1_0 = 34'b0; data_in_1_1 = 34'b0;
		valid_in_0_1 = 1'b0; valid_in_1_0 = 1'b0; valid_in_1_1 = 1'b0;
		credit_in_0_1 = 1'b0; credit_in_1_0 = 1'b0; credit_in_1_1 = 1'b0;
	
		data_in_0_0 = {`HEAD,3'b001,3'b001,{13{`HEAD}}};
		valid_in_0_0 = 1'b1;
		credit_in_0_0 = 1'b1;
		`TICK
		`CYCLE
		data_in_0_0 = {`BODY,3'b001,3'b001,{13{`BODY}}};
		`CYCLE
		data_in_0_0 = {`TAIL,3'b001,3'b001,{13{`TAIL}}};
		`CYCLE
		data_in_0_0 = 34'b0;
		valid_in_0_0 = 1'b0;
		credit_in_0_0 = 1'b0;
		
		#100
		
	
		$finish;
	end
	
	
	
	
	
	/* Router Testbench 
	initial begin
		#1000
		$finish;
	end
	
   wire valid_to_om, ready_to_source, valid_to_QE, credits_from_queue_e,valid_to_RE,credits_from_queue_ce, valid_from_om_w; 
   wire [`DATA_WIDTH-1:0] data_to_om, data_to_queue_e,data_to_queue_re,data_from_om_w;
   wire valid_to_sink, credit_from_sink_to_router, credits_to_om_w, valid_from_queue_to_sink;
	wire [`DATA_WIDTH-1:0] data_to_sink, data_from_queue_to_sink;
	
	wire valid_to_om_ST, ready_to_source_ST, credits_from_queue_cs, valid_to_QS, valid_to_RS, credits_from_queue_s;
	wire [`DATA_WIDTH-1:0] data_to_om_ST, data_to_queue_s, data_to_queue_rs;

	router #(.router_id_x(2), .router_id_y(2)) ROUTER (
       	.clock(CLK), .reset(RESET),
       	.data_to_queue_e(data_to_queue_re), 
       	.data_to_queue_s(data_to_queue_rs), .data_to_queue_w(34'b0), .data_to_queue_n(34'b0), .data_to_queue_h(34'b0), 
       	.valid_to_queue_e(valid_to_RE),  .valid_to_queue_s(valid_to_RS), .valid_to_queue_w(1'b0), .valid_to_queue_n(1'b0), .valid_to_queue_h(1'b0), 
       	.credits_to_om_e(1'b0), .credits_to_om_s(1'b0), .credits_to_om_w(credits_to_om_w), .credits_to_om_n(1'b0), .credits_to_om_h(1'b0),
       	
       //outputs are as follows
       .data_from_om_e(), .data_from_om_s(), .data_from_om_n(), .data_from_om_w(data_from_om_w), .data_from_om_h(), 
       .valid_from_om_e(), .valid_from_om_s(), .valid_from_om_n(), .valid_from_om_w(valid_from_om_w), .valid_from_om_h(),
       
       .credits_from_QE(credits_from_queue_e), .credits_from_QS(credits_from_queue_s), .credits_from_QW(), .credits_from_QN(), .credits_from_QH()
   );
   

	// FROM EAST	
   // fake souce and output module
   source #(.id_bit(1'b0)) S (.clock(CLK), .v_o(valid_to_om), .r_i(ready_to_source), .d_o (data_to_om), .reset(RESET));
	output_module outMod (.clock(CLK), .data_out(data_to_queue_e), .credit_in(credits_from_queue_ce), .valid_out(valid_to_QE),
                              .source_valid(valid_to_om), .ready_to_source(ready_to_source), .data_from_source(data_to_om), .reset(RESET));

	// channel from fake source to router
   mc_channel #(.width(35), .depth(1), .depth_sz(1)) DCE(.clock(CLK), .d_in({valid_to_QE,data_to_queue_e}), .d_out({valid_to_RE,data_to_queue_re}), .reset(RESET) );
   mc_channel #(.width(1), .depth(1), .depth_sz(1)) CCE(.clock(CLK), .d_in(credits_from_queue_e), .d_out(credits_from_queue_ce), .reset(reset) );


	// channel from router to fake sink
   mc_channel #(.width(35), .depth(1), .depth_sz(1)) DCS(.clock(CLK), .d_in({valid_from_om_w,data_from_om_w}), .d_out({valid_to_sink,data_to_sink}), .reset(RESET) );
   mc_channel #(.width(1), .depth(1), .depth_sz(1)) CCS(.clock(CLK), .d_in(credit_from_sink_to_router), .d_out(credits_to_om_w), .reset(reset) );

	// sink	
	queue SYNCQ (.clock(CLK), .reset(RESET), .i_d(data_to_sink), .i_v(valid_to_sink), .credits_back(credit_from_sink_to_router), 
                  .o_d(data_from_queue_to_sink), .o_v(valid_from_queue_to_sink), .o_r(1'b1));          

	sync destination (.clock(CLK), .d_i(data_from_queue_to_sink), .v_i(valid_from_queue_to_sink), .credit_out(), .clear_to_send(1'b1), .reset(RESET));
	
	// FROM SOUTH
	// FROM EAST	
   // fake souce and output module
   source #(.id_bit(1'b1)) S_ST (.clock(CLK), .v_o(valid_to_om_ST), .r_i(ready_to_source_ST), .d_o (data_to_om_ST), .reset(RESET));
	output_module outMod_ST (.clock(CLK), .data_out(data_to_queue_s), .credit_in(credits_from_queue_cs), .valid_out(valid_to_QS),
                              .source_valid(valid_to_om_ST), .ready_to_source(ready_to_source_ST), .data_from_source(data_to_om_ST), .reset(RESET));

	// channel from fake source to router
   mc_channel #(.width(35), .depth(1), .depth_sz(1)) DCSs(.clock(CLK), .d_in({valid_to_QS,data_to_queue_s}), .d_out({valid_to_RS,data_to_queue_rs}), .reset(RESET) );
   mc_channel #(.width(1), .depth(1), .depth_sz(1)) CCSs(.clock(CLK), .d_in(credits_from_queue_s), .d_out(credits_from_queue_cs), .reset(reset) );

	*/
	
	
	/* Final Control Testbench 
	reg [1:0] e_type, s_type;
	reg valid_e, valid_s;
	reg om_ready_w;
	
	control #(.router_id_x(2), .router_id_y(2)) CTRL (
		.clock(CLK), .reset(RESET),
		.dest_e({{3'd2},{3'd2}}), .type_e(e_type), .valid_e(valid_e), .om_ready_e(1'b1),
		.dest_s({{3'b0},{3'b0}}), .type_s(e_type), .valid_s(valid_s), .om_ready_s(1'b1), 
		.dest_w(6'b0), .type_w(), .valid_w(1'b0), .om_ready_w(om_ready_w),
		.dest_n(6'b0), .type_n(), .valid_n(1'b0), .om_ready_n(1'b1),
		.dest_h(6'b0), .type_h(), .valid_h(1'b0), .om_ready_h(1'b1),

		.q_ready_e(), .q_ready_s(), .q_ready_w(), .q_ready_n(), .q_ready_h(),	// ready outputs back to input queues
		.sel_e(), .sel_s(), .sel_w(), .sel_n(), .sel_h()								// crossbar selector outputs
	);
	initial begin
		e_type = `HEAD;
		valid_e = 1'b1;
		s_type = `HEAD;
		valid_s = 1'b1;
		om_ready_w = 1'b1;
		`TICK
		`CYCLE
		e_type = `BODY;
		`CYCLE
		e_type = `TAIL;
		`CYCLE
		e_type = `EMPTY;
		s_type = `HEAD;
		valid_e = 1'b0;
		`CYCLE
		s_type = `BODY;
		`CYCLE
		s_type = `TAIL;
		`CYCLE
		valid_s = 1'b0;
		s_type = `EMPTY;
		valid_e = 1'b1;
		e_type = `HEAD;
		`CYCLE
		e_type = `BODY;
		`CYCLE
		e_type = `TAIL;
		`CYCLE
		
		`CYCLE
		$finish();
	end
	*/
	

	/* Control testbench
	reg [2:0] route;
	reg [1:0] type;
	control  #(.id(`EAST)) CONTROL ( 
		.clock(CLK), .reset(RESET), 
		.route_e(route), .route_s(`NO_ROUTE), .route_w(`NO_ROUTE), .route_n(`NO_ROUTE), .route_h(`NO_ROUTE),			// requested route in; only makes sense if flit is header
		.type_e(type), .type_s(`EMPTY), .type_w(`EMPTY), .type_n(`EMPTY), .type_h(`EMPTY)														// type of header
	
	);
 

	initial begin
		// first packet, destined east
		`TICK
		route = `EAST;
		type = `HEAD;
		`CYCLE
		route = `WEST;
		type = `BODY;
		`CYCLE
		route = `NORTH;
		type = `BODY;
		`CYCLE
		route = `SOUTH;
		type = `TAIL;
		`CYCLE
		
		// second packet, destined west
		route = `WEST;
		type = `HEAD;
		`CYCLE
		route = `EAST;
		type = `BODY;
		`CYCLE
		route = `NORTH;
		type = `BODY;
		`CYCLE
		route = `SOUTH;
		type = `TAIL;
		`CYCLE
		
	
		$finish;
	end
	
	 */


	/* Arbiter Testbench
	wire [4:0] grants;
	reg [4:0] request;
	arbiter_5x5 ARBITER(.clock(CLK), .reset(RESET), .request(request), .grant(grants));

	initial begin
		request = 5'b0;
		`CYCLE
		`CYCLE
		request = 5'b1;
		`CYCLE
		`CYCLE
		request = 5'b01001;
		`CYCLE
		request = `TICK 5'b01000;
		`CYCLE
		`CYCLE
		request = 5'b11111;
		#30
		request = 5'b11101;
		#70
	
		$finish;
	end
	*/


endmodule

